#include "h_func.h"
#include "q_perm.h"
#include "matrix.h"
#include "tables.h"
#include "mul_tab.h"
#include "field.h"

#include <assert.h>

void split_word(word value, byte* result, int result_size)
{
	int i;
	assert(result_size >= 4);

	word mask = 0xff;
	for(i = 0; i < 4; ++i)
	{
		//Because of endian issue,not using pointer to get value
		result[i] = value & mask;
		value >>= 8;
	}
}

word join_word(byte* values)
{
	int i;
	word result = 0;

	result ^= values[3];
	for(i = 2; i >= 0; --i)
	{
		result <<= 8;
		result ^= values[i];
	}
	return result;
}

byte qxxx_perm(byte value, byte xor1, byte xor2, byte (*perm_funcs[3])(byte) )
{
	byte result;
	result = perm_funcs[0](value) ^ xor1; 
	result = perm_funcs[1](result) ^ xor2;
	result = perm_funcs[2](result);
	return result;
}

byte q001_perm(byte value, byte xor1, byte xor2)
{
	static byte (*perm_funcs[3])(byte) = {
		q0_perm, q0_perm, q1_perm
	};
	return qxxx_perm(value, xor1, xor2, perm_funcs);
}

byte q100_perm(byte value, byte xor1, byte xor2)
{
	static byte (*perm_funcs[3])(byte) = {
		q1_perm, q0_perm, q0_perm
	};
	return qxxx_perm(value, xor1, xor2, perm_funcs);
}

byte q011_perm(byte value, byte xor1, byte xor2)
{
	static byte (*perm_funcs[3])(byte) = {
		q0_perm, q1_perm, q1_perm
	};
	return qxxx_perm(value, xor1, xor2, perm_funcs);
}


byte q110_perm(byte value, byte xor1, byte xor2)
{
	static byte (*perm_funcs[3])(byte) = {
		q1_perm, q1_perm, q0_perm
	};
	return qxxx_perm(value, xor1, xor2, perm_funcs);
}

byte gf_256_169_mul(byte lhs, byte rhs)
{
	return element_mul(lhs, rhs, gf_256_169_mul_tab, gf_256_169_element_num);
}

word h_func(word value, word* list_word, int list_size)
{
	int i;
	static byte (*high_perm_funcs[2][4])(byte) = {
		{q1_perm, q1_perm, q0_perm, q0_perm,},
		{q1_perm, q0_perm, q0_perm, q1_perm,}
	};

	static byte (*low_trans_funcs[])(byte, byte, byte) = {
		q001_perm, q100_perm, q011_perm, q110_perm
	};


	static const int tmp_bytes_size = 4;
	byte tmp_bytes[tmp_bytes_size];
	byte tmp_list_bytes[tmp_bytes_size];
	byte tmp_list_bytes2[tmp_bytes_size];

	assert(list_size >= 2);
	assert(list_size <= 4);

	split_word(value, tmp_bytes, tmp_bytes_size);

	int currentRound = list_size - 1;
	for(; currentRound >= 2; currentRound--)
	{
		split_word(list_word[currentRound], tmp_list_bytes, tmp_bytes_size);
		for( i = 0; i < 4; ++i)
		{
			tmp_bytes[i] = high_perm_funcs[currentRound - 2][i](tmp_bytes[i]) ^ tmp_list_bytes[i];
		}
	}

	split_word(list_word[1], tmp_list_bytes2, tmp_bytes_size);
	split_word(list_word[0], tmp_list_bytes, tmp_bytes_size);
	for( i = 0; i < 4; ++i)
	{
		tmp_bytes[i] = low_trans_funcs[i](tmp_bytes[i], tmp_list_bytes2[i], tmp_list_bytes[i]);
	}

//	printf("Before mds:%x%x%x%x\n", tmp_bytes[3], tmp_bytes[2], tmp_bytes[1], tmp_bytes[0]);
	
	byte tmp_result[tmp_bytes_size];
	byte_matrix_mul(mds, 4, 4, tmp_bytes, 4, 1, tmp_result, tmp_bytes_size, element_add, gf_256_169_mul);

	word result;
	result = join_word(tmp_result);

	return result;
}

#include <stdio.h>

void test_split_word()
{
	printf("Start testing split_word........\n");
	
	byte result[4];
	word value = 0x01020304;
	split_word(value, result, 4);
	assert(result[0] == 0x04);
	assert(result[1] == 0x03);
	assert(result[2] == 0x02);
	assert(result[3] == 0x01);

	printf("Test split_word complete!\n");
}

void test_join_word()
{
	printf("Start testing join_word.......\n");

	byte values[4] = {
		0x4,0x3,0x2,0x1
	};
	word result = join_word(values);
	assert(result == 0x01020304);

	printf("Test join_word complete!\n");
}

void test_h_func()
{
	printf("Start testing h_func.........\n");

	word value = 0;
	word list_word[4] = {
		0,0,0,0
	};
	word result = h_func(value, list_word, sizeof(list_word) / sizeof(list_word[0]) );
	assert( result == 0xd4750c54);

	printf("Test h_func complete!\n");
}
